<?php

namespace Lib;

final class Misc {
    
    /**
     * 对array_column扩展,事实上扩展了 $mixed 允许接收boolean(true)
     * 
     * @param array $input
     * @param string|boolean $mixed
     * @param string $indexKey
     * 
     * @return array
     */
    public static function arrayColumn(array $input, $mixed = true, $indexKey = null) {
        if($mixed === true) {
            $newInput = [];
            
            foreach ($input as $index => $subInput) {
                if(array_key_exists($indexKey, $subInput)) {
                    $newInput[$subInput[$indexKey]][$index] = $subInput;
                }
            }
            
            return $newInput;
        }
        
        return \array_column($input, ($mixed === false ? null : $mixed), $indexKey);
    }

    /**
     * 检测IP地址是否在配置规则内
     *
     * 注意: 如果规则为空时, 返回 true
     *
     * @param string $ip    ipv4的格式
     * @param array $rules  规则, 例如: 192.168.1.1/24, 127.0.0.1
     * @return boolean
     */
    public static function isInNetwork($ip, array $rules) {
        if (!isset($rules[0])) {
            return true;
        }

        $pass  = false;
        $rules = array_flip(array_flip($rules));

        foreach ($rules as $rule) {
            $rule = trim($rule);

            if(empty($rule)) {
                continue;
            }

            $set     = explode('/', trim($rule));
            $netway  = (int) ((isset($set[1])) ? max(1, min($set[1], 32)) : 32);
            $ip_rule = $set[0];

            if ($netway === 32 && strcmp($ip_rule, $ip) === 0) {
                $pass = true;
                break;
            }

            $bits = 32 - $netway;

            $netmask = (0xFFFFFFFF >> $bits) << $bits;

            $ip_source = ip2long($ip_rule) & $netmask;
            $ip_target = ip2long($ip) & $netmask;

            if (strcmp($ip_target, $ip_source) === 0) {
                $pass = true;
                break;
            }
        }

        return $pass;
    }

    /**
     * 获取客户端的 IP
     *
     * @param boolean $ip2long 是否转换成为整形
     *
     * @return int|string
     */
    public static function getClientIp($ip2long = false) {
        if (isset($_SERVER)) {
            $ip = \filter_input(\INPUT_SERVER, 'HTTP_X_REAL_IP');
            
            if (empty($ip)) {
                $tmp = explode(',', (string)\filter_input(\INPUT_SERVER, 'HTTP_X_FORWARDED_FOR'));
                $ip  = array_pop($tmp) ?: (
                    \filter_input(\INPUT_SERVER, 'HTTP_CLIENT_IP') ?: \filter_input(\INPUT_SERVER, 'REMOTE_ADDR')
                );
            }
        } else {
            $ip = getenv('HTTP_X_REAL_IP');
            
            if(empty($ip)){
                $tmp = explode(',', (string)getenv('HTTP_X_FORWARDED_FOR'));
                $ip  = array_pop($tmp) ?: (getenv('HTTP_CLIENT_IP') ?: getenv('REMOTE_ADDR'));
            }
        }

        return $ip2long ? sprintf("%u", ip2long($ip)) : $ip;
    }
    
    /**
     * 完整路径
     * 
     * @param string $path
     * @return string
     */
    public static function realPath($path) {
        return APPLICATION_PATH . DS . ltrim($path, '/');
    }

    /**
     * 获取客户端的 UA
     *
     * @return string
     */
    public static function getClientUa() {
        return isset($_SERVER['HTTP_USER_AGENT']) ? self::filter($_SERVER['HTTP_USER_AGENT']) : '';
    }

    /**
     * 字符串处理
     *
     * @param type $str
     * @return string
     */
    public static function filter($str) {
        return addslashes(htmlentities(trim($str)));
    }

    /**
     * url http
     *
     * @param string $url
     * @return string
     */
    public static function urlComplete($url) {
        $url = trim($url);
        
        return ((stripos($url, 'http://') === false && stripos($url, 'https://') === false && !empty($url)) ? 'http://' . $url : $url);
    }

    /**
     * 数据签名
     *
     * @param array $params
     * @return string
     */
    public static function sign(array $params, $sep = ':'){
        ksort($params);

        $str  = implode($sep, $params);
        $hash = sha1($str . ':' . strrev($str) . ':' . (count($params) << 8));

        return md5($hash . $sep . substr($hash, 5, 21));
    }

    /**
     * 检测域名/URL是否在白名单中
     *
     * @staticvar \Yaf\Config\Ini $_whitelist
     * @param STRING $url
     * @return boolean
     * @throws \Exception
     */
    public static function isWhiteDomain($url) {
        static $_whitelist = null;

        if (!$_whitelist instanceof \Yaf\Config\Ini) {
            $_environ   = \Yaf\Application::app()->environ();
            $_whitelist = new \Yaf\Config\Ini(\Lib\Misc::realPath('conf/ipwhitelist.ini'), $_environ);
        }

        if (!$_whitelist instanceof \Yaf\Config\Ini) {
            throw new \Exception('域名白名单设置异常');
        }

        $writeArr = $_whitelist->get('list');

        if ($writeArr) {
            $domain = parse_url($url, PHP_URL_HOST);

            if (in_array($domain, $writeArr->toArray())) {
                return true;
            }
        }

        return false;
    }

    /**
     * 替换指定的字符为 #
     *
     * 慎用
     *
     * @param string $subjects
     * @param array $find
     * @return string
     */
    public static function strReplace($subjects, $find = array()) {
        if (!$find) {
            $find = array('>', '<', '/*', '*/', '"', '\'', ')', '(', '}', '{', '\\x', '\\u', '`', '&#', '^', ';', '\\');
        }

        $uri = trim(str_ireplace($find, '#', $subjects));

        if (mb_strlen($uri) < 2083){
            while (strpos($uri, '##') > 0) {
                $uri = str_replace('##', '#', $uri);
            }
        }

        return $uri;
    }

    /**
     * 生成一串随机码
     *
     * @param int $length
     * @param boolean $case
     * @return string
     */
    public static function randCode($length = 12, $case = true) {
        $str = 'abcdefghijklnmopqsrtvuwxyz123456879';

        if($case === true) {
            $str .= 'ABCDFEGHIJKLMNOPRQSTUVWXYZ';
        }

        $slen = strlen($str);
        $nstr = array();

        while ($length > 0) {
            $index = mt_rand(0, $slen);

            if(isset($str[$index])) {
                $nstr[] = $str[$index];
                $length--;
            }
        }

        return implode($nstr);
    }

    /**
     * 可逆加/解密
     *
     * @param string $string
     * @param string $operation
     * @param string $key
     * @param int $expiry
     * @return string
     */
    public static function authCode($string, $operation = 'DECODE', $key = '', $expiry = 0) {
        $ckey_length = 4;
        $key = md5($key ? $key : \Yaf\Registry::get('config')->get('resource.user.common.authcode'));

        $keya = md5(substr($key, 0, 16));
        $keyb = md5(substr($key, 16, 16));
        $keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length) : substr(md5(microtime()), -$ckey_length)) : '';
        $cryptkey = $keya . md5($keya . $keyc);
        $key_length = strlen($cryptkey);
        $string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) : sprintf('%010d', $expiry ? $expiry + time() : 0) . substr(md5($string . $keyb), 0, 16) . $string;
        $string_length = strlen($string);
        $result = '';
        $box = range(0, 255);
        $rndkey = array();
        for ($i = 0; $i <= 255; $i++) {
            $rndkey[$i] = ord($cryptkey[$i % $key_length]);
        }
        for ($j = $i = 0; $i < 256; $i++) {
            $j = ($j + $box[$i] + $rndkey[$i]) % 256;
            $tmp = $box[$i];
            $box[$i] = $box[$j];
            $box[$j] = $tmp;
        }
        for ($a = $j = $i = 0; $i < $string_length; $i++) {
            $a = ($a + 1) % 256;
            $j = ($j + $box[$a]) % 256;
            $tmp = $box[$a];
            $box[$a] = $box[$j];
            $box[$j] = $tmp;
            $result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));
        }
        if ($operation == 'DECODE') {
            if ((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26) . $keyb), 0, 16)) {
                return substr($result, 26);
            } else {
                return '';
            }
        } else {
            return $keyc . str_replace('=', '', base64_encode($result));
        }
    }
}
